using System;
using System.Reflection;

using Microsoft.Practices.CompositeWeb.Utility;

namespace WCSFContrib.Extensions.EventBroker
{
	/// <summary>
	/// Abstract common base to Publisher and Subscriber
	/// </summary>
	/// <typeparam name="T">Either an EventInfo or a MethodInfo</typeparam>
	public abstract class EventBrokingData<T> : IEquatable<EventBrokingData<T>> where T : MemberInfo
	{
		protected WeakReference instanceRef;
		protected MemberReference<T> memberTopicData;

		/// <summary>
		/// Raised when it is detected that the instance is no longer alive.
		/// </summary>
		public event EventHandler Died;

		public EventBrokingData(object instance, string topic, T memberInfo)
			: this(instance, new MemberReference<T>(topic, memberInfo))
		{
			Guard.ArgumentNotNull(instance, "instance");
			Guard.ArgumentNotNull(topic, "topic");
			Guard.ArgumentNotNull(memberInfo, "memberInfo");
		}

		public EventBrokingData(object instance, MemberReference<T> memberTopicData)
		{
			Guard.ArgumentNotNull(instance, "instance");
			Guard.ArgumentNotNull(memberTopicData, "memberTopicData");

			instanceRef = new WeakReference(instance);
			this.memberTopicData = memberTopicData;
		}

		public object Instance
		{
			get
			{
				// Call IsAlive on get to make sure Died invoked if instanceRef is dead.
				return IsAlive ? instanceRef.Target : null;
			}
		}

		public string Topic
		{
			get
			{
				return memberTopicData.Topic;
			}
		}

		public T MemberInfo
		{
			get
			{
				return memberTopicData.Info;
			}
		}

		protected bool IsAlive
		{
			get
			{
				if (!instanceRef.IsAlive)
					OnDeceased();
				return instanceRef.IsAlive;
			}
		}

		private void OnDeceased()
		{
			if (Died != null)
				Died.Invoke(this, EventArgs.Empty);
		}

		#region .Equals() implementation
		public override bool Equals(object obj)
		{
			Subscriber o = obj as Subscriber;
			return o != null && Equals(o);
		}

		public bool Equals(EventBrokingData<T> other)
		{
			if (!IsAlive)
				return false;
			object instance = Instance;

			return Topic.Equals(other.Topic) &&
					 memberTopicData.Info.Equals(other.MemberInfo) &&
					 instance.Equals(other.Instance);
		}

		public override int GetHashCode()
		{
			return Topic.GetHashCode() ^
					 MemberInfo.GetHashCode() ^
					 instanceRef.Target.GetHashCode();
		}
		#endregion
	}
}